/*
 * Copyright (c) 2010-2011, Petr Panteleyev
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.wordpress;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.TreeMap;
import java.util.concurrent.ExecutionException;

import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.JEditorPane;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.SwingWorker;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import org.panteleyev.blogapi.BlogEntry;
import org.panteleyev.blogapi.WordPressAccount;
import org.panteleyev.blogapi.WordPressComment;
import org.panteleyev.blogapi.WordPressCommentCount;
import org.panteleyev.blogapi.WordPressCommentStatus;
import org.panteleyev.blogclient.BloggerFrame;
import org.panteleyev.blogclient.Icon;
import org.panteleyev.blogclient.MainClass;
import org.panteleyev.blogclient.Options;
import org.panteleyev.blogclient.events.PostDeletedEvent;
import org.panteleyev.swing.OSUtil;
import org.panteleyev.swing.SwingFactory;
import org.panteleyev.utilities.Updatable;
import org.panteleyev.utilities.events.AppEventListener;

/**
 *
 * @author Petr Panteleyev <petr@panteleyev.org>
 */
public class WPCommentsFrame extends BloggerFrame<WordPressUserProfile> implements Updatable {

    private class CommentsTreeCellRenderer extends DefaultTreeCellRenderer {
        CommentsTreeCellRenderer() {
        }

        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value,
                                           boolean selected,
                                           boolean expanded,
                                           boolean leaf,
                                           int row,
                                           boolean hasFocus) {

            super.getTreeCellRendererComponent(tree, value,
                                           selected,
                                           expanded,
                                           leaf,
                                           row,
                                           hasFocus);

            StringBuilder text = new StringBuilder();

            DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
            Object userObject = node.getUserObject();
            if (userObject instanceof WordPressComment) {
                WordPressComment comm = (WordPressComment)userObject;

                String user = comm.getAuthor();

                text.append("<html><b>");
                text.append(user);
                text.append("</b>&nbsp;");

                if (comm.getDateCreated() != null) {
                    text.append(WPCommentsFrame.dateFormat.format(comm.getDateCreated()));
                    text.append(" ");
                }

                text.append("</html>");
            }

            setText(text.toString());
            return this;
        }
    }

    private AbstractAction          newAction;
    private AbstractAction          replyAction;
    private AbstractAction          editAction;
    private AbstractAction          deleteAction;
    private AbstractAction          spamAction;
    private AbstractAction          approveAction;
    private AbstractAction          holdAction;

    private BlogEntry               entry;
    private WordPressAccount        account;
    private CommentHandler          commHandler;

    private DefaultMutableTreeNode  treeRoot = new DefaultMutableTreeNode();
    private DefaultTreeModel        treeModel = new DefaultTreeModel(treeRoot);
    private JTree                   commentsTreeView = new JTree();
    private JEditorPane             commentView = new JEditorPane();
    private JScrollPane             treeScroller;


    private static SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy kk:mm");

    public WPCommentsFrame(WordPressUserProfile profile, BlogEntry entry) {
        super(profile);
        setIconImage(Icon.COMMENTS.I().getImage());

        this.entry = entry;
        account = getProfile().getAccount();
        commHandler = new CommentHandler(account, this);

        createActions();
        initComponents();
        createMenuBar();
        createPopupMenu();

        StringBuilder b = new StringBuilder(L10N.COMMENTS_FRAME_TITLE.s());
        b.append(" [");
        b.append(entry.getId());
        b.append("] ");
        if (entry.getSubject() != null) {
            b.append(entry.getSubject());
        }
        setTitle(b.toString());

        addAppEventListener(PostDeletedEvent.class, new AppEventListener() {
            @Override
            public void eventReceived(EventObject event) {
                if (((PostDeletedEvent)event).getPost().equals(WPCommentsFrame.this.entry)) {
                    closeFrame(true);
                }
            }
        });

        update();
    }

    public WordPressAccount getAccount() {
        return account;
    }

    private void createActions() {
        newAction = new CommentHandler.CommentNewAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onNewComment();
            }
        };

        replyAction = new CommentHandler.CommentReplyAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onReply();
            }
        };

        editAction = new CommentHandler.CommentEditAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onEditComment();
            }
        };

        deleteAction = new CommentHandler.CommentDeleteAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onDeleteComment();
            }
        };

        spamAction = new CommentHandler.CommentSpamAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onSpamComments();
            }
        };

        approveAction = new CommentHandler.CommentApproveAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onApproveComments();
            }
        };

        holdAction = new CommentHandler.CommentHoldAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onHoldComments();
            }
        };
    }

    private void initComponents() {
        getContentPane().setLayout(new BorderLayout());

        getContentPane().add(
            SwingFactory.createToolBarPanel(
                SwingFactory.createToolBar(false, true,
                    newAction,
                    replyAction,
                    editAction,
                    deleteAction
                ),
                Box.createHorizontalStrut(5),
                SwingFactory.createToolBar(false, true,
                    spamAction,
                    approveAction,
                    holdAction
                ),
                Box.createHorizontalStrut(5),
                createProfileToolBar(false, true),
                Box.createHorizontalGlue(),
                SwingFactory.createToolBar(false, true, getUpdateAction()),
                Box.createHorizontalStrut(5),
                getBusyLabel(),
                Box.createHorizontalStrut(5)
            ), BorderLayout.NORTH
        );

        JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT);

        commentsTreeView.setInheritsPopupMenu(true);
        commentsTreeView.setModel(treeModel);
        commentsTreeView.setRootVisible(false);
        commentsTreeView.setShowsRootHandles(true);
        commentsTreeView.setVisibleRowCount(10);
        commentsTreeView.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent evt) {
                commentsTreeViewValueChanged(evt);
            }
        });
        commentsTreeView.setCellRenderer(new CommentsTreeCellRenderer());

        commentView.setContentType("text/html");
        commentView.setEditable(false);
        commentView.addHyperlinkListener(new HyperlinkListener() {
            @Override
            public void hyperlinkUpdate(HyperlinkEvent e) {
                if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
                    OSUtil.runBrowser(e.getURL().toString());
                }
            }
        });

        treeScroller = new JScrollPane(commentsTreeView);
        split.setTopComponent(treeScroller);
        JScrollPane scroll = new JScrollPane(commentView);
        split.setBottomComponent(scroll);

        getContentPane().add(split, BorderLayout.CENTER);

        Options.getBounds(this);
    }

    private void createMenuBar() {
        createMenuBar(
            createFileMenu(),
            createMenu(L10N.COMMENTS_MENU_NAME.s(), null,
                newAction,
                null,
                replyAction,
                editAction,
                null,
                deleteAction,
                null,
                spamAction,
                approveAction,
                holdAction
            ),
            createProfileMenu(),
            createToolsMenu(),
            createWindowMenu(),
            createHelpMenu()
        );
    }

    private void createPopupMenu() {
        treeScroller.setComponentPopupMenu(createMenu(null, null,
                newAction,
                null,
                replyAction,
                editAction,
                null,
                deleteAction,
                null,
                spamAction,
                approveAction,
                holdAction
        ).getPopupMenu());
    }

    @Override
    public void update() {
        treeRoot.removeAllChildren();

        getBusyLabel().setBusy(true);

        new SwingWorker<Object,Object>() {
            @Override
            protected Object doInBackground() throws Exception {
                WordPressCommentCount commCount = getAccount().getCommentCount(entry);
                ArrayList<WordPressComment> comments = getAccount().getComments(entry.getBlog(), entry, null, 0, commCount.getTotal());

                TreeMap<Integer,DefaultMutableTreeNode> commHash = new TreeMap<>();

                for (WordPressComment c : comments) {
                    commHash.put(c.getId(), new DefaultMutableTreeNode(c));
                }

                /*
                 * Build tree
                 */
                for (Integer key : commHash.keySet()) {
                    DefaultMutableTreeNode tNode = commHash.get(key);
                    int parentID = ((WordPressComment)tNode.getUserObject()).getParentId();

                    if (parentID == 0) {
                        treeRoot.add(tNode);
                    } else {
                        DefaultMutableTreeNode pNode = commHash.get(parentID);
                        if (pNode == null) {
                            treeRoot.add(tNode);
                        } else {
                            pNode.add(tNode);
                        }
                    }
                }

                return null;
            }

            @Override
            protected void done() {
                try {
                    get();
                    treeModel.reload();
                    commentView.setText("");
                } catch (InterruptedException | ExecutionException ex) {
                    handleException(ex);
                } finally {
                    enableActions(true);
                    getBusyLabel().setBusy(false);
                }
            }
        }.execute();
    }

    String getItemID() {
        return entry.getId();
    }

    private ArrayList<WordPressComment> getSelectedComments() {
        ArrayList<WordPressComment> result = new ArrayList<>();

        TreePath[] paths = commentsTreeView.getSelectionPaths();
        if (paths != null) {
            for (TreePath p : paths) {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode)p.getLastPathComponent();
                if (node != null) {
                    Object userObject = node.getUserObject();
                    if (userObject instanceof WordPressComment) {
                        result.add((WordPressComment)userObject);
                    }
                }
            }
        }

        return result;
    }

    private WordPressComment getSelectedComment() {
        WordPressComment result = null;

        TreePath path = commentsTreeView.getSelectionPath();
        if (path != null) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
            if (node != null) {
                Object userObject = node.getUserObject();
                if (userObject instanceof WordPressComment) {
                    return (WordPressComment)userObject;
                }
            }
        }

        return result;
    }

    private void commentsTreeViewValueChanged(TreeSelectionEvent evt) {
        commentView.setText(commHandler.commentToString(getSelectedComment()));
        enableActions(getUpdateAction().isEnabled());
    }

    private void setCommentsStatus(WordPressCommentStatus status) {
        ArrayList<WordPressComment> toEdit = getSelectedComments();

        if (!toEdit.isEmpty() && MainClass.showConfirmDialog(this)) {
            for (WordPressComment c : toEdit) {
                c.setStatus(status);
            }

            getBusyLabel().setBusy(true);
            enableActions(false);
            commHandler.editComments(entry.getBlog(), toEdit);
        }
    }

    private void onSpamComments() {
        setCommentsStatus(WordPressCommentStatus.SPAM);
    }

    private void onApproveComments() {
        setCommentsStatus(WordPressCommentStatus.APPROVE);
    }

    private void onHoldComments() {
        setCommentsStatus(WordPressCommentStatus.HOLD);
    }

    private void onEditComment() {
        WordPressComment comm = getSelectedComment();
        if (comm != null) {
            CommentDialog dial = new CommentDialog(this, comm);
            if (dial.showDialog()) {
                comm.setAuthor(dial.getAuthor());
                comm.setAuthorEmail(dial.getAuthorEmail());
                comm.setAuthorUrl(dial.getAuthorUrl());
                comm.setContent(dial.getContent());
                comm.setStatus(dial.getStatus());

                getBusyLabel().setBusy(true);
                enableActions(false);
                commHandler.editComment(entry.getBlog(), comm);
            }
        }
    }

    private void onNewComment() {
        WordPressComment comm = new WordPressComment();

        CommentDialog dial = new CommentDialog(this, comm);
        if (dial.showDialog()) {
            comm.setAuthor("");
            comm.setAuthorEmail("");
            comm.setAuthorUrl("");
            comm.setContent(dial.getContent());

            getBusyLabel().setBusy(true);
            enableActions(false);
            commHandler.newComment(entry.getBlog(), entry.getId(), comm, 0);
        }
    }

    private void onReply() {
        WordPressComment replyTo = getSelectedComment();
        if (replyTo != null) {
            WordPressComment comm = new WordPressComment();

            CommentDialog dial = new CommentDialog(this, comm);
            if (dial.showDialog()) {
                comm.setAuthor("");
                comm.setAuthorEmail("");
                comm.setAuthorUrl("");
                comm.setContent(dial.getContent());

                getBusyLabel().setBusy(true);
                enableActions(false);
                commHandler.newComment(entry.getBlog(), entry.getId(), comm, replyTo.getId());
            }
        }
    }

    private void onDeleteComment() {
        ArrayList<WordPressComment> toDelete = getSelectedComments();

        if (!toDelete.isEmpty() && MainClass.showConfirmDialog(this)) {
            getBusyLabel().setBusy(true);
            enableActions(false);
            commHandler.deleteComments(entry.getBlog(), toDelete);
        }
    }

    private void enableActions(boolean b) {
        if (b) {
            newAction.setEnabled(true);
            TreePath[] paths = commentsTreeView.getSelectionPaths();
            boolean single = paths != null && paths.length == 1;
            editAction.setEnabled(single);
            replyAction.setEnabled(single);
            boolean multiple = paths != null && paths.length > 0;
            deleteAction.setEnabled(multiple);
            spamAction.setEnabled(multiple);
            approveAction.setEnabled(multiple);
            holdAction.setEnabled(multiple);
        } else {
            newAction.setEnabled(false);
            editAction.setEnabled(false);
            replyAction.setEnabled(false);
            deleteAction.setEnabled(false);
            spamAction.setEnabled(false);
            approveAction.setEnabled(false);
            holdAction.setEnabled(false);
        }
        getUpdateAction().setEnabled(b);
    }
}
